package com.mgy.api.framework.core.parameter;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mgy.api.framework.core.constant.Constant;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.text.ParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 方法参数解析
 *
 * @author mgy
 * @date 2019.10.20
 */
@Slf4j
public abstract class AbstractParameterParse {

    private final ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();

    protected HttpServletRequest request;
    protected HttpServletResponse response;

    public AbstractParameterParse() {

    }

    public AbstractParameterParse(HttpServletRequest request, HttpServletResponse response) {
        this.request = request;
        this.response = response;
    }

    public String getAppKey() {
        return request.getParameter(Constant.APP_KEY);
    }

    public String getFormat() {
        return request.getParameter(Constant.FORMAT);
    }

    public String getTimestamp() {
        return request.getParameter(Constant.TIMESTAMP);
    }

    public String getApiName() {
        return request.getParameter(Constant.METHOD);
    }

    public String getVersion() {
        return request.getParameter(Constant.VERSION);
    }

    public String getSign() {
        return request.getParameter(Constant.SIGN);
    }


    public String getResponseName() {
        return request.getParameter(Constant.METHOD).replace(".", "_") + "_response";
    }

    public Object[] getParameterValues(Method method) {
        String[] paramNameArr = paramNameDiscoverer.getParameterNames(method);
        Parameter[] parameterArr = method.getParameters();
        if (paramNameArr == null || paramNameArr.length == 0 || parameterArr == null || paramNameArr.length != parameterArr.length) {
            return null;
        }
        int length = paramNameArr.length;
        Object[] args = new Object[length];
        for (int i = 0; i < length; i++) {
            args[i] = parseParameter(parameterArr[i], paramNameArr[i]);
        }
        return args;
    }

    protected Object getSimpleParameterValue(Class targetClass, String paramVal) {
        if (paramVal == null) {
            return null;
        }
        if (targetClass == String.class) {
            return paramVal;
        } else if (targetClass == Integer.class) {
            return Integer.parseInt(paramVal);
        } else if (targetClass == Long.class) {
            return Long.parseLong(paramVal);
        } else if (targetClass == Byte.class) {
            return Byte.parseByte(paramVal);
        } else if (targetClass == Boolean.class) {
            return Boolean.parseBoolean(paramVal);
        } else if (targetClass == Float.class) {
            return Float.parseFloat(paramVal);
        } else if (targetClass == Double.class) {
            return Double.parseDouble(paramVal);
        } else if (targetClass == Short.class) {
            return Short.parseShort(paramVal);
        } else if (targetClass == Date.class) {
            if (NumberUtils.isDigits(paramVal)) {
                return new Date(Long.parseLong(paramVal));
            } else {
                try {
                    return DateUtils.parseDate(paramVal, "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd");
                } catch (ParseException e) {
                    log.error("时间转换出错", e);
                }
            }
        }
        return null;
    }


    protected Object getRequestBody(Class paramType) {
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            InputStream inputStream = request.getInputStream();
            byte[] buf = new byte[1024];
            int len;
            while ((len = inputStream.read(buf)) > -1) {
                byteArrayOutputStream.write(buf, 0, len);
            }
            byte[] bytes = byteArrayOutputStream.toByteArray();
            if (bytes != null && bytes.length > 0) {
                return JSONObject.parseObject(new String(bytes, getRequestCharEncoding()), paramType);
            }
        } catch (IOException e) {
            log.error("参数解析错误：", e);
        }
        return null;
    }

    protected Object getFormParameter(Class<?> paramType) {
        Map<String, String[]> requestParamMap = request.getParameterMap();
        if (requestParamMap == null || requestParamMap.isEmpty()) {
            return null;
        }
        Map<String, String> paramMap = new HashMap<>(requestParamMap.size());
        for (String key : requestParamMap.keySet()) {
            paramMap.put(key, requestParamMap.get(key)[0]);
        }
        return JSONObject.parseObject(JSON.toJSONString(paramMap), paramType);
    }

    protected String getRequestCharEncoding() {
        String charEncoding = request.getCharacterEncoding();
        return charEncoding == null ? "UTF-8" : charEncoding;
    }


    protected Object parseParameter(Parameter parameter, String paramName) {
        Class paramType = parameter.getType();
        if (paramType == HttpServletRequest.class) {
            return request;
        } else if (paramType == HttpServletResponse.class) {
            return response;
        } else if (paramType == MultipartFile.class) {
            return getMultipartFile();
        } else if (paramType == MultipartHttpServletRequest.class) {
            return getMultipartHttpServletRequest();
        }

        if (BeanUtils.isSimpleValueType(paramType)) {
            RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
            if (requestParam != null) {
                paramName = requestParam.value();
            }
            return getSimpleParameterValue(paramType, request.getParameter(paramName));
        } else if (parameter.getAnnotation(RequestBody.class) != null) {
            return getRequestBody(paramType);
        }
        return getFormParameter(paramType);
    }


    protected abstract MultipartFile getMultipartFile();

    protected abstract MultipartHttpServletRequest getMultipartHttpServletRequest();


}